home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 September / PCWorld_2008-09_cd.bin / v cisle / sadanastroju / lightning-0.8-tb-win.xpi / chrome / calendar.jar / content / calendar / calendar-month-view.xml < prev    next >
Extensible Markup Language  |  2008-03-13  |  63KB  |  1,767 lines

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!--
  3.    - ***** BEGIN LICENSE BLOCK *****
  4.    - Version: MPL 1.1/GPL 2.0/LGPL 2.1
  5.    -
  6.    - The contents of this file are subject to the Mozilla Public License Version
  7.    - 1.1 (the "License"); you may not use this file except in compliance with
  8.    - the License. You may obtain a copy of the License at
  9.    - http://www.mozilla.org/MPL/
  10.    -
  11.    - Software distributed under the License is distributed on an "AS IS" basis,
  12.    - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13.    - for the specific language governing rights and limitations under the
  14.    - License.
  15.    -
  16.    - The Original Code is calendar views.
  17.    -
  18.    - The Initial Developer of the Original Code is
  19.    -   Oracle Corporation
  20.    - Portions created by the Initial Developer are Copyright (C) 2005
  21.    - the Initial Developer. All Rights Reserved.
  22.    -
  23.    - Contributor(s):
  24.    -   Vladimir Vukicevic <vladimir@pobox.com>
  25.    -   Stefan Sitter <ssitter@googlemail.com>
  26.    -   Clint Talbert <cmtalbert@myfastmail.com>
  27.    -   Michael B├╝ttner <michael.buettner@sun.com>
  28.    -   Philipp Kewisch <mozilla@kewis.ch>
  29.    -   Markus Adrario <MarkusAdrario@web.de>
  30.    -
  31.    - Alternatively, the contents of this file may be used under the terms of
  32.    - either the GNU General Public License Version 2 or later (the "GPL"), or
  33.    - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  34.    - in which case the provisions of the GPL or the LGPL are applicable instead
  35.    - of those above. If you wish to allow use of your version of this file only
  36.    - under the terms of either the GPL or the LGPL, and not to allow others to
  37.    - use your version of this file under the terms of the MPL, indicate your
  38.    - decision by deleting the provisions above and replace them with the notice
  39.    - and other provisions required by the GPL or the LGPL. If you do not delete
  40.    - the provisions above, a recipient may use your version of this file under
  41.    - the terms of any one of the MPL, the GPL or the LGPL.
  42.    -
  43.    - ***** END LICENSE BLOCK *****
  44. -->
  45.  
  46. <!-- Note that this file depends on helper functions in calUtils.js-->
  47.  
  48. <bindings id="calendar-month-view-bindings"
  49.   xmlns="http://www.mozilla.org/xbl"
  50.   xmlns:html="http://www.w3.org/1999/xhtml"
  51.   xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
  52.   xmlns:xbl="http://www.mozilla.org/xbl">
  53.  
  54.   <binding id="calendar-month-day-box-item" extends="chrome://calendar/content/calendar-view-core.xml#calendar-editable-item">
  55.     <content>
  56.       <xul:vbox flex="1">
  57.         <xul:hbox>
  58.           <xul:image anonid="shadow-left-image" class="calendar-event-box-shadow-left"/>
  59.           <xul:box anonid="event-container" flex="1">
  60.             <xul:box class="calendar-event-selection" orient="horizontal" flex="1">
  61.               <xul:stack anonid="eventbox"
  62.                          class="calendar-event-box-container"
  63.                          flex="1">
  64.                 <xul:hbox class="calendar-event-details">
  65.                   <xul:image anonid="item-icon"
  66.                              class="calendar-month-day-box-item-image"/>
  67.                   <xul:label anonid="item-label"
  68.                              class="calendar-month-day-box-item-label"
  69.                              xbl:inherits="context"/>
  70.                   <xul:vbox align="left"
  71.                             flex="1"
  72.                             xbl:inherits="context">
  73.                     <xul:label anonid="event-name"
  74.                                crop="end"
  75.                                flex="1"
  76.                                style="margin: 0;"/>
  77.                     <xul:textbox anonid="event-name-textbox"
  78.                                  class="plain"
  79.                                  crop="end"
  80.                                  hidden="true"
  81.                                  style="background: transparent !important;"
  82.                                  wrap="true"/>
  83.                     <xul:spacer flex="1"/>
  84.                   </xul:vbox>
  85.                   <xul:vbox pack="center">
  86.                     <xul:image anonid="alarm-image"
  87.                                class="alarm-image"
  88.                                xbl:inherits="flashing"
  89.                                hidden="true"/>
  90.                   </xul:vbox>
  91.                 </xul:hbox>
  92.                 <xul:image anonid="gradient"
  93.                            class="calendar-event-box-gradient"
  94.                            hidden="true" height="1px"/>
  95.               </xul:stack>
  96.             </xul:box>
  97.             <xul:calendar-category-box anonid="category-box"/>
  98.           </xul:box>
  99.           <xul:image anonid="shadow-right-image" class="calendar-event-box-shadow-right"/>
  100.         </xul:hbox>
  101.         <xul:box anonid="shadow-container" orient="horizontal">
  102.           <xul:image class="calendar-event-box-shadow-edge-left"/>
  103.           <xul:image class="calendar-event-box-shadow-bottom" flex="1"/>
  104.           <xul:image class="calendar-event-box-shadow-edge-right"/>
  105.         </xul:box>
  106.       </xul:vbox>
  107.     </content>
  108.     <implementation>
  109.       <constructor><![CDATA[
  110.         var gradient = document.getAnonymousElementByAttribute(this,
  111.                                                                "anonid",
  112.                                                                "gradient");
  113.         var container = document.getAnonymousElementByAttribute(this,
  114.                                                                 "anonid",
  115.                                                                 "event-container");
  116.         if (gradient) {
  117.           gradient.removeAttribute("hidden");
  118.         }
  119.  
  120.         container.setAttribute("class", "calendar-item");
  121.         container.setAttribute("class", this.getAttribute("class"));
  122.         container.setAttribute("item-calendar", this.getAttribute("item-calendar"));
  123.         this.removeAttribute("class");
  124.         this.removeAttribute("item-calendar");
  125.       ]]>
  126.       </constructor>
  127.  
  128.       <property name="occurrence">
  129.         <getter><![CDATA[
  130.             return this.mOccurrence;
  131.         ]]></getter>
  132.         <setter><![CDATA[
  133.           this.mOccurrence = val;
  134.           var icon = document.getAnonymousElementByAttribute(this,"anonid","item-icon");                                                              
  135.           if (isEvent(val)) {
  136.             icon.setAttribute("type", "event");
  137.             if (val.startDate.isDate) {
  138.               icon.setAttribute("type", "all-day");
  139.             } else {
  140.               var label = document.getAnonymousElementByAttribute(this,"anonid","item-label");
  141.               var df = Components.classes["@mozilla.org/calendar/datetime-formatter;1"].
  142.                        getService(Components.interfaces.calIDateTimeFormatter);
  143.               var timezone = this.calendarView? this.calendarView.mTimezone:
  144.                              calendarDefaultTimezone();
  145.               label.value = df.formatTime(
  146.               val.startDate.getInTimezone(timezone));
  147.               label.setAttribute("time", "true");
  148.             }
  149.           } else if (isToDo(val)) {
  150.             icon.setAttribute("type", "todo");
  151.             if (val.isCompleted) {
  152.               icon.setAttribute("completed", "true");
  153.             }
  154.           }
  155.  
  156.           this.setEditableLabel();
  157.           this.setCSSClasses();
  158.           return val;
  159.         ]]></setter>
  160.       </property>
  161.       <property name="parentBox"
  162.                 onget="return this.mParentBox;"
  163.                 onset="this.mParentBox = val;"/>
  164.       <method name="removeShadows">
  165.         <body><![CDATA[
  166.           removeAnonymousElement(this, "shadow-left-image");
  167.           removeAnonymousElement(this, "shadow-right-image");
  168.           removeAnonymousElement(this, "shadow-container");
  169.         ]]></body>
  170.       </method>
  171.     </implementation>
  172.  
  173.     <handlers>
  174.       <handler event="draggesture"><![CDATA[
  175.         var dragService = Components.classes["@mozilla.org/widget/dragservice;1"].
  176.                           getService(Components.interfaces.nsIDragService);
  177.         var transfer = Components.classes["@mozilla.org/widget/transferable;1"].
  178.                        createInstance(Components.interfaces.nsITransferable);
  179.         transfer.addDataFlavor("text/calendar");
  180.  
  181.         var item = this.occurrence;
  182.         if (!isCalendarWritable(item.calendar)) {
  183.             return;
  184.         }
  185.  
  186.         var flavourProvider = {
  187.             QueryInterface: function(aIID) {
  188.                 ensureIID(
  189.                     [ Components.interfaces.nsIFlavorDataProvider,
  190.                       Components.interfaces.nsISupports], aIID);
  191.                 return this;
  192.             },
  193.             item: item,
  194.  
  195.             getFlavorData: function(aInTransferable, aInFlavor, aOutData, aOutDataLen) {
  196.                 if ((aInFlavor == "application/vnd.x-moz-cal-event") ||
  197.                     (aInFlavor == "application/vnd.x-moz-cal-task")) {
  198.                     aOutData.value = this.item;
  199.                     aOutDataLen.value = 1;
  200.                 } else {
  201.                     ASSERT(false, "error:"+aInFlavor);
  202.                 }
  203.             }
  204.         };
  205.  
  206.         if (isEvent(item)) {
  207.           transfer.addDataFlavor("application/vnd.x-moz-cal-event");
  208.           transfer.setTransferData("application/vnd.x-moz-cal-event", flavourProvider, 0);
  209.         } else if (isToDo(item)) {
  210.           transfer.addDataFlavor("application/vnd.x-moz-cal-task");
  211.           transfer.setTransferData("application/vnd.x-moz-cal-task", flavourProvider, 0);
  212.         }
  213.  
  214.         // Also set some normal data-types, in case we drag into another app
  215.         var serializer = Components.classes["@mozilla.org/calendar/ics-serializer;1"].
  216.                                     createInstance(Components.interfaces.calIIcsSerializer);
  217.         serializer.addItems([item], 1);
  218.  
  219.         var supportsString = Components.classes["@mozilla.org/supports-string;1"].
  220.                              createInstance(Components.interfaces.nsISupportsString);
  221.         supportsString.data = serializer.serializeToString();
  222.         transfer.setTransferData("text/calendar", supportsString, supportsString.data.length*2);
  223.         transfer.setTransferData("text/unicode", supportsString, supportsString.data.length*2);
  224.  
  225.         var action = dragService.DRAGDROP_ACTION_MOVE;
  226.         var supArray = Components.classes["@mozilla.org/supports-array;1"].
  227.                        createInstance(Components.interfaces.nsISupportsArray);
  228.         supArray.AppendElement(transfer);
  229.  
  230.         // OK, now that the data is all set up, start playing with the shadows
  231.         this.parentBox.monthView.doDeleteItem(item);
  232.  
  233.         // Figure out how many shadows we're going to need, and how they should
  234.         // be oriented from the mouse's position
  235.         var boxes = this.parentBox.monthView.findBoxesForItem(item);
  236.         this.parentBox.monthView.mShadowLength = boxes.length;
  237.         for (var i in boxes) {
  238.             if (boxes[i].box == this.parentBox) {
  239.                 this.parentBox.monthView.mShadowIndex = i;
  240.                 break;
  241.             }
  242.         }
  243.         this.parentBox.addDropShadows();
  244.  
  245.         // Mozilla doesn't provide any generic way for us to listen for the
  246.         // drag-end.  Add this listener for a best approximation of listening
  247.         // for that case.
  248.         var monthbox = this.parentBox;
  249.         monthbox.monthView.dropListener = function checkStillDragging() {
  250.             var dragService = Components.classes["@mozilla.org/widget/dragservice;1"].
  251.                               getService(Components.interfaces.nsIDragService);
  252.             var session;
  253.             try {
  254.                 session = dragService.getCurrentSession();
  255.             } catch (ex) {}
  256.             if (!session) {
  257.                 monthbox.removeDropShadows();
  258.                 monthbox.addItem(item);
  259.                 window.removeEventListener("mouseover",
  260.                                            monthbox.monthView.dropListener,
  261.                                            true);
  262.             }
  263.         };
  264.         window.addEventListener("mouseover",
  265.                                 monthbox.monthView.dropListener, true);
  266.  
  267.         dragService.invokeDragSession(this, supArray, null, action);
  268.       ]]></handler>
  269.     </handlers>
  270.   </binding>
  271.  
  272.   <binding id="calendar-month-day-box">
  273.     <content>
  274.       <xul:vbox flex="1">
  275.         <xul:label anonid="day-label" crop="end" class="calendar-month-day-box-date-label"/>
  276.         <xul:vbox anonid="day-items"/>
  277.       </xul:vbox>
  278.     </content>
  279.  
  280.     <implementation>
  281.       <field name="mDate">null</field>
  282.       <!-- mItemData will always be kept sorted in display order -->
  283.       <field name="mItemData">[]</field>
  284.       <field name="mMonthView">null</field>
  285.       <field name="mShowMonthLabel">false</field>
  286.  
  287.       <property name="date">
  288.         <getter>return this.mDate;</getter>
  289.         <setter>this.setDate(val); return val;</setter>
  290.       </property>
  291.  
  292.       <property name="monthView">
  293.         <getter><![CDATA[
  294.           return this.mMonthView;
  295.         ]]></getter>
  296.         <setter><![CDATA[
  297.           this.mMonthView = val;
  298.           return val;
  299.         ]]></setter>
  300.       </property>
  301.  
  302.       <property name="selected">
  303.         <getter><![CDATA[
  304.           var sel = this.getAttribute("selected");
  305.           if (sel && sel == "true") {
  306.             return true;
  307.           }
  308.  
  309.           return false;
  310.         ]]></getter>
  311.         <setter><![CDATA[
  312.           if (val)
  313.             this.setAttribute("selected", "true");
  314.           else
  315.             this.removeAttribute("selected");
  316.           return val;
  317.         ]]></setter>
  318.       </property>
  319.  
  320.       <property name="dayitems">
  321.         <getter>return document.getAnonymousElementByAttribute(this, "anonid", "day-items");</getter>
  322.       </property>
  323.  
  324.       <property name="showMonthLabel">
  325.         <getter><![CDATA[
  326.           return this.mShowMonthLabel;
  327.         ]]></getter>
  328.         <setter><![CDATA[
  329.           if (this.mShowMonthLabel == val) {
  330.             return val;
  331.           }
  332.           this.mShowMonthLabel = val;
  333.  
  334.           if (!this.mDate) {
  335.             return val;
  336.           }
  337.           var daylabel = document.getAnonymousElementByAttribute(this, "anonid", "day-label");
  338.  
  339.           if (val) {
  340.             var monthName = calGetString("dateFormat", "month." + (this.mDate.month+1) + ".Mmm");
  341.             daylabel.setAttribute("value", this.mDate.day + " " + monthName);
  342.           } else {
  343.             daylabel.setAttribute("value", this.mDate.day);
  344.           }
  345.           return val;
  346.         ]]></setter>
  347.       </property>
  348.  
  349.       <method name="setDate">
  350.         <parameter name="aDate"/>
  351.         <body><![CDATA[
  352.           if (!aDate)
  353.             throw Components.results.NS_ERROR_NULL_POINTER;
  354.  
  355.           // Remove all the old events
  356.           this.mItemData = new Array();
  357.           while(this.dayitems.hasChildNodes()) {
  358.                 this.dayitems.removeChild(this.dayitems.lastChild);
  359.           }
  360.  
  361.           if (this.mDate && this.mDate.compare(aDate) == 0)
  362.             return;
  363.  
  364.           this.mDate = aDate;
  365.           var daylabel = document.getAnonymousElementByAttribute(this, "anonid", "day-label");
  366.  
  367.           if (this.mShowMonthLabel)
  368.           {
  369.             var monthName = calGetString("dateFormat", "month." + (aDate.month+1) + ".Mmm");
  370.             daylabel.setAttribute("value", aDate.day + " " + monthName);
  371.           } else {
  372.             daylabel.setAttribute("value", aDate.day);
  373.           }
  374.         ]]></body>
  375.       </method>
  376.  
  377.       <method name="addItem">
  378.         <parameter name="aItem"/>
  379.         <body><![CDATA[
  380.           for each (ed in this.mItemData) {
  381.             if (aItem.hashId == ed.item.hashId)
  382.             {
  383.               this.deleteItem(aItem);
  384.               break;
  385.             }
  386.           }
  387.  
  388.           function comptor(a, b) {
  389.             var aIsEvent = isEvent(a.item);
  390.             var aIsTodo = isToDo(a.item);
  391.  
  392.             var bIsEvent = isEvent(b.item);
  393.             var bIsTodo = isToDo(b.item);
  394.  
  395.             if ((!aIsEvent && !aIsTodo) || (!bIsEvent && !bIsTodo)) {
  396.               // XXX ????
  397.               dump ("Don't know how to sort these two events: " + a.item + " " + b.item + "\n");
  398.               return 0;
  399.             }
  400.  
  401.             // sort todos before events
  402.             if (aIsTodo && bIsEvent) return -1;
  403.             if (aIsEvent && bIsTodo) return 1;
  404.  
  405.             // XXX how do I sort todos?
  406.             if (aIsTodo && bIsTodo) {
  407.               return 0;
  408.             }
  409.  
  410.             if (aIsEvent && bIsEvent) {
  411.               // sort all day events before events with a duration
  412.               if (a.item.startDate.isDate && !b.item.startDate.isDate) return -1;
  413.               if (!a.item.startDate.isDate && b.item.startDate.isDate) return 1;
  414.  
  415.               var cmp;
  416.  
  417.               cmp = a.item.startDate.compare(b.item.startDate);
  418.               if (cmp != 0)
  419.                 return cmp;
  420.  
  421.               cmp = a.item.endDate.compare(b.item.endDate);
  422.               if (cmp != 0)
  423.                 return cmp;
  424.  
  425.               if (a.item.title < b.item.title)
  426.                 return -1;
  427.               if (a.item.title > b.item.title)
  428.                 return 1;
  429.             }
  430.  
  431.             return 0;
  432.           }
  433.  
  434.           // insert the new item block (while keeping the array sorted),
  435.           // and then relayout.
  436.           // Note: don't use Array.Sort, because that's quicksort, which
  437.           // is not good for an already mostly-sorted array
  438.           var newItem = {item: aItem};
  439.           for (i = 0; i<this.mItemData.length; ++i) {
  440.             if (comptor(this.mItemData[i], newItem) > 0)
  441.               break;
  442.           }
  443.           this.mItemData.splice(i, 0, newItem);
  444.  
  445.           this.relayout();
  446.         ]]></body>
  447.       </method>
  448.  
  449.       <method name="selectItem">
  450.         <parameter name="aItem"/>
  451.         <body><![CDATA[
  452.           for each (var itd in this.mItemData) {
  453.               if (aItem && (itd.item.hashId == aItem.hashId)) {
  454.                   itd.box.selected = true;
  455.               }
  456.           }
  457.         ]]></body>
  458.       </method>
  459.  
  460.       <method name="unselectItem">
  461.         <parameter name="aItem"/>
  462.         <body><![CDATA[
  463.           for each (var itd in this.mItemData) {
  464.               if (aItem && (itd.item.hashId == aItem.hashId)) {
  465.                   itd.box.selected = false;
  466.               }
  467.           }
  468.         ]]></body>
  469.       </method>
  470.  
  471.       <method name="deleteItem">
  472.         <parameter name="aItem"/>
  473.         <body><![CDATA[
  474.           var deleted = [];
  475.  
  476.           var origLen = this.mItemData.length;
  477.           this.mItemData = this.mItemData.filter(
  478.             function(itd) {
  479.               if (aItem.hashId == itd.item.hashId)
  480.               {
  481.                 deleted.push(itd);
  482.                 return false;
  483.               }
  484.               return true;
  485.             });
  486.  
  487.           if (deleted.length > 0) {
  488.             for each (itd in deleted) {
  489.               if (itd.box)
  490.                 this.dayitems.removeChild(itd.box);
  491.             }
  492.             // no need to relayout; all we did was delete
  493.             //this.relayout();
  494.           }
  495.         ]]></body>
  496.       </method>
  497.  
  498.       <method name="relayout">
  499.         <body><![CDATA[
  500.           function createXULElement(el) {
  501.               return document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", el);
  502.           }
  503.  
  504.  
  505.           for (var i = 0; i < this.mItemData.length; i++) {
  506.             var itd = this.mItemData[i];
  507.  
  508.             if (!itd.box) {
  509.               // find what element to insert before
  510.               var before = null;
  511.               for (var j = i+1; !before && this.mItemData[j]; j++)
  512.                 before = this.mItemData[j].box;
  513.  
  514.               var box = createXULElement("calendar-month-day-box-item");
  515.               box.setAttribute("context", this.getAttribute("item-context") || this.getAttribute("context"));
  516.               box.setAttribute("class", "calendar-item");
  517.               box.setAttribute("item-calendar", itd.item.calendar.uri.spec);
  518.               box.setAttribute("tooltip", "itemTooltip");
  519.  
  520.               if (itd.item.hashId in this.monthView.mFlashingEvents) {
  521.                 box.setAttribute("flashing", "true");
  522.               }
  523.  
  524.               var categoriesSelectorList = "";
  525.               var categoriesProperty = itd.item.getProperty("CATEGORIES");
  526.               if (categoriesProperty) {
  527.                 var categoriesArray = categoriesStringToArray(categoriesProperty);
  528.                 var cssClassesArray = categoriesArray.map(formatStringForCSSRule);
  529.                 categoriesSelectorList = cssClassesArray.join(" ");
  530.               }
  531.  
  532.               box.setAttribute("item-category", categoriesSelectorList);
  533.  
  534.               this.dayitems.insertBefore(box, before);
  535.  
  536.               box.calendarView = this.monthView;
  537.               box.item = itd.item;
  538.               box.occurrence = itd.item;
  539.               box.parentBox = this;
  540.               itd.box = box;
  541.             }
  542.           }
  543.         ]]></body>
  544.       </method>
  545.  
  546.       <!-- While you might expect 'dragexit' to be fired when we drag outside
  547.          - the node in question, this isn't actually the case.  So, we need 
  548.          - to keep track of these shadows on the month-view itself, so they can
  549.          - be properly removed.
  550.         -->
  551.       <method name="addDropShadows">
  552.         <body><![CDATA[
  553.           // Only allow one set of drop-boxes
  554.           if (this.monthView.mDropShadows) {
  555.               return;
  556.           }
  557.  
  558.           this.monthView.mDropShadows = [];
  559.           var shadowStart = this.mDate.clone();
  560.           shadowStart.day -= this.monthView.mShadowIndex;
  561.           for (var i = 0; i < this.monthView.mShadowLength; i++) {
  562.               var dropbox =  document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "box");
  563.               dropbox.setAttribute("dropbox", "true");
  564.               dropbox.setAttribute("flex", "1");
  565.  
  566.               var box = this.monthView.findBoxForDate(shadowStart);
  567.               if (!box) {
  568.                   // Dragging to the end of a view
  569.                   shadowStart.day += 1;
  570.                   continue;
  571.               }
  572.               box.box.dayitems.insertBefore(dropbox, box.box.dayitems.firstChild);
  573.               this.monthView.mDropShadows.push(dropbox);
  574.  
  575.               shadowStart.day += 1;
  576.           }
  577.         ]]></body>
  578.       </method>
  579.       <method name="removeDropShadows">
  580.         <body><![CDATA[
  581.           if (!this.monthView.mDropShadows) {
  582.               return;
  583.           }
  584.  
  585.           for each (shadow in this.monthView.mDropShadows) {
  586.               shadow.parentNode.removeChild(shadow);
  587.           }
  588.  
  589.           this.monthView.mDropShadows = null;
  590.         ]]></body>
  591.       </method>
  592.     </implementation>
  593.  
  594.     <handlers>
  595.       <handler event="mousedown"><![CDATA[
  596.         event.stopPropagation();
  597.  
  598.         if (this.mDate)
  599.           this.monthView.selectedDay = this.mDate;
  600.       ]]></handler>
  601.       <handler event="dblclick"><![CDATA[
  602.         event.stopPropagation();
  603.         this.monthView.controller.createNewEvent();
  604.       ]]></handler>
  605.       <handler event="dragenter"><![CDATA[
  606.         var dragService = Components.classes["@mozilla.org/widget/dragservice;1"].
  607.                           getService(Components.interfaces.nsIDragService);
  608.         var session = dragService.getCurrentSession();
  609.         if (session) {
  610.             session.canDrop = true;
  611.             this.removeDropShadows();
  612.             this.addDropShadows();
  613.         }
  614.       ]]></handler>
  615.  
  616.       <handler event="dragover"><![CDATA[
  617.         var dragService = Components.classes["@mozilla.org/widget/dragservice;1"].
  618.                           getService(Components.interfaces.nsIDragService);
  619.         var session = dragService.getCurrentSession();
  620.         session.canDrop = true;
  621.       ]]></handler>
  622.       <handler event="dragexit"><![CDATA[
  623.         if (event.originalTarget != this) {
  624.             return;
  625.         }
  626.         this.removeDropShadows();
  627.         var dragService = Components.classes["@mozilla.org/widget/dragservice;1"].
  628.                           getService(Components.interfaces.nsIDragService);
  629.         var session = dragService.getCurrentSession();
  630.         session.canDrop = false;
  631.       ]]></handler>
  632.  
  633.       <handler event="dragdrop"><![CDATA[
  634.          var dragService = Components.classes["@mozilla.org/widget/dragservice;1"].
  635.                            getService(Components.interfaces.nsIDragService);
  636.          var session = dragService.getCurrentSession();
  637.  
  638.          var transfer = Components.classes["@mozilla.org/widget/transferable;1"].
  639.                         createInstance(Components.interfaces.nsITransferable);
  640.          transfer.addDataFlavor("application/x-moz-cal-event");
  641.          session.getData(transfer, 0);
  642.          var flavor = {};
  643.          var data = {};
  644.  
  645.          // nsITransferable sucks when it comes to trying to add extra flavors.
  646.          // This will throw NS_ERROR_FAILURE, so as a workaround, we use the 
  647.          // sourceNode property and get the event that way
  648.          //transfer.getAnyTransferData(flavor, data, {});
  649.  
  650.          var newStart;
  651.          var newEnd;
  652.  
  653.          var boxDate = this.mDate;
  654.  
  655.          if (!session.sourceNode || !session.sourceNode.occurrence) {
  656.              return;
  657.          }
  658.  
  659.          event.stopPropagation();
  660.          var item = session.sourceNode.occurrence;
  661.          var beginMove = session.sourceNode.mParentBox.date;
  662.          var duration = boxDate.subtractDate(beginMove);
  663.  
  664.          // Since both boxDate and beginMove are dates (note datetimes),
  665.          // subtractDate will only give us a non-zero number of hours on DST
  666.          // changes. While strictly speaking, subtractDate's behavior is 
  667.          // correct, we need to move the event a discrete number of days here.
  668.          if (duration.hours == 23) {
  669.              // crossing DT/DST border (spring)
  670.              duration.hours++;
  671.              duration.normalize();
  672.          } else if (duration.hours == 1) {
  673.             // crossing DST/DT border (autumn)
  674.             duration.hours--;
  675.          }
  676.  
  677.         if (isEvent(item)) {
  678.             newStart = item.startDate.clone();
  679.             newStart.addDuration(duration);
  680.             newEnd = item.endDate.clone();
  681.             newEnd.addDuration(duration);
  682.          } else if (isToDo(item)) {
  683.              if (item.entryDate) {
  684.                  newStart = item.entryDate.clone();
  685.                  newStart.addDuration(duration);
  686.              }
  687.              if (item.dueDate) {
  688.                  newEnd = item.dueDate.clone();
  689.                  newEnd.addDuration(duration);
  690.              }
  691.          }
  692.  
  693.          this.removeDropShadows();
  694.  
  695.          window.removeEventListener("mouseover",
  696.                                    this.monthView.dropListener,
  697.                                    true);
  698.  
  699.          this.monthView.controller.modifyOccurrence(item, newStart, newEnd);
  700.       ]]></handler>
  701.  
  702.       <handler event="click" button="0"><![CDATA[
  703.         this.monthView.setSelectedItems(0, []);
  704.       ]]></handler>
  705.     </handlers>
  706.   </binding>
  707.  
  708.   <binding id="calendar-month-view-column-header">
  709.     <content>
  710.       <xul:hbox flex="1">
  711.         <xul:spacer flex="1"/>
  712.         <xul:label anonid="label" crop="right" class="calendar-month-view-column-header-label" />
  713.         <xul:spacer flex="1"/>
  714.       </xul:hbox>
  715.     </content>
  716.  
  717.     <implementation>
  718.       <field name="mIndex">-1</field>
  719.  
  720.       <constructor><![CDATA[
  721.         if (this.mIndex == -1) {
  722.           var attrIndex = this.getAttribute("index");
  723.           if (attrIndex)
  724.             this.index = parseInt(attrIndex);
  725.         }
  726.       ]]></constructor>
  727.  
  728.       <property name="index">
  729.         <getter>return this.mIndex;</getter>
  730.         <setter><![CDATA[
  731.           this.mIndex = val % 7;
  732.  
  733.           var label = document.getAnonymousElementByAttribute(this, "anonid", "label");
  734.           var dayName = calGetString("dateFormat", "day." + (this.mIndex+1) + ".name");
  735.           label.setAttribute("value", dayName);
  736.  
  737.           return this.mIndex;
  738.         ]]></setter>
  739.       </property>
  740.     </implementation>
  741.   </binding>
  742.  
  743.   <binding id="calendar-month-view">
  744.     <content>
  745.       <xul:vbox flex="1">
  746.         <xul:hbox anonid="headerbox" equalsize="always"/>
  747.  
  748.         <xul:grid anonid="monthgrid" flex="1">
  749.           <xul:columns anonid="monthgridcolumns" equalsize="always">
  750.             <xul:column flex="1" class="calendar-month-view-grid-column"/>
  751.             <xul:column flex="1" class="calendar-month-view-grid-column"/>
  752.             <xul:column flex="1" class="calendar-month-view-grid-column"/>
  753.             <xul:column flex="1" class="calendar-month-view-grid-column"/>
  754.             <xul:column flex="1" class="calendar-month-view-grid-column"/>
  755.             <xul:column flex="1" class="calendar-month-view-grid-column"/>
  756.             <xul:column flex="1" class="calendar-month-view-grid-column"/>
  757.           </xul:columns>
  758.  
  759.           <xul:rows anonid="monthgridrows" equalsize="always">
  760.             <xul:row flex="1" class="calendar-month-view-grid-row">
  761.               <xul:calendar-month-day-box/>
  762.               <xul:calendar-month-day-box/>
  763.               <xul:calendar-month-day-box/>
  764.               <xul:calendar-month-day-box/>
  765.               <xul:calendar-month-day-box/>
  766.               <xul:calendar-month-day-box/>
  767.               <xul:calendar-month-day-box/>
  768.             </xul:row>
  769.             <xul:row flex="1" class="calendar-month-view-grid-row">
  770.               <xul:calendar-month-day-box/>
  771.               <xul:calendar-month-day-box/>
  772.               <xul:calendar-month-day-box/>
  773.               <xul:calendar-month-day-box/>
  774.               <xul:calendar-month-day-box/>
  775.               <xul:calendar-month-day-box/>
  776.               <xul:calendar-month-day-box/>
  777.             </xul:row>
  778.             <xul:row flex="1" class="calendar-month-view-grid-row">
  779.               <xul:calendar-month-day-box/>
  780.               <xul:calendar-month-day-box/>
  781.               <xul:calendar-month-day-box/>
  782.               <xul:calendar-month-day-box/>
  783.               <xul:calendar-month-day-box/>
  784.               <xul:calendar-month-day-box/>
  785.               <xul:calendar-month-day-box/>
  786.             </xul:row>
  787.             <xul:row flex="1" class="calendar-month-view-grid-row">
  788.               <xul:calendar-month-day-box/>
  789.               <xul:calendar-month-day-box/>
  790.               <xul:calendar-month-day-box/>
  791.               <xul:calendar-month-day-box/>
  792.               <xul:calendar-month-day-box/>
  793.               <xul:calendar-month-day-box/>
  794.               <xul:calendar-month-day-box/>
  795.             </xul:row>
  796.             <xul:row flex="1" class="calendar-month-view-grid-row">
  797.               <xul:calendar-month-day-box/>
  798.               <xul:calendar-month-day-box/>
  799.               <xul:calendar-month-day-box/>
  800.               <xul:calendar-month-day-box/>
  801.               <xul:calendar-month-day-box/>
  802.               <xul:calendar-month-day-box/>
  803.               <xul:calendar-month-day-box/>
  804.             </xul:row>
  805.             <xul:row flex="1" class="calendar-month-view-grid-row">
  806.               <xul:calendar-month-day-box/>
  807.               <xul:calendar-month-day-box/>
  808.               <xul:calendar-month-day-box/>
  809.               <xul:calendar-month-day-box/>
  810.               <xul:calendar-month-day-box/>
  811.               <xul:calendar-month-day-box/>
  812.               <xul:calendar-month-day-box/>
  813.             </xul:row>
  814.           </xul:rows>
  815.         </xul:grid>
  816.       </xul:vbox>
  817.     </content>
  818.  
  819.     <implementation implements="calICalendarView">
  820.  
  821.       <!-- constructor -->
  822.       <constructor><![CDATA[
  823.           function createXULElement(el) {
  824.             return document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", el);
  825.           }
  826.  
  827.           var headerbox = document.getAnonymousElementByAttribute(this, "anonid", "headerbox")
  828.           for (var i = 0; i < 7; i++) {
  829.             var hdr = createXULElement("calendar-month-view-column-header");
  830.             hdr.setAttribute("flex", "1");
  831.             headerbox.appendChild(hdr);
  832.             hdr.index = i;
  833.           }
  834.  
  835.           this.mTimezone = floating();
  836.           var alarmService = Components.classes['@mozilla.org/calendar/alarm-service;1']
  837.                              .getService(Components.interfaces.calIAlarmService);
  838.           alarmService.addObserver(this.mObserver);
  839.       ]]></constructor>
  840.  
  841.       <destructor><![CDATA[
  842.         if (this.mCalendar) {
  843.             this.mCalendar.removeObserver(this.mObserver);
  844.         }
  845.         var alarmService = Components.classes['@mozilla.org/calendar/alarm-service;1']
  846.                            .getService(Components.interfaces.calIAlarmService);
  847.         alarmService.removeObserver(this.mObserver);
  848.       ]]></destructor>
  849.  
  850.       <!-- fields -->
  851.  
  852.       <field name="mCalendar">null</field>
  853.       <field name="mController">null</field>
  854.       <field name="mStartDate">null</field>
  855.       <field name="mEndDate">null</field>
  856.       <field name="mDateBoxes">null</field>
  857.       <field name="mTimezone">null</field>
  858.  
  859.       <field name="mSelectedItems">[]</field>
  860.       <field name="mSelectedDayBox">null</field>
  861.  
  862.       <field name="mShowDaysOutsideMonth">true</field>
  863.       <field name="mTasksInView">false</field>
  864.       <field name="mShowCompleted">false</field>
  865.       <field name="mShowFullMonth">true</field>
  866.       <field name="mWeekStartOffset">0</field>
  867.       <field name="mDaysOffArray">[0,6]</field>
  868.       <field name="mDisplayDaysOff">true</field>
  869.       <field name="mDropShadows">null</field>
  870.       <field name="mShadowIndex">0</field>
  871.       <field name="mFlashingEvents">new Object()</field>
  872.  
  873.       <!-- other methods -->
  874.       <method name="setAttribute">
  875.         <parameter name="aAttr"/>
  876.         <parameter name="aVal"/>
  877.         <body><![CDATA[
  878.           var needsrelayout = false;
  879.           if (aAttr == "context" || aAttr == "item-context")
  880.               needsrelayout = true;
  881.  
  882.           var ret = XULElement.prototype.setAttribute.call (this, aAttr, aVal);
  883.  
  884.           if (needsrelayout)
  885.               this.relayout();
  886.  
  887.           return ret;
  888.         ]]></body>
  889.       </method>
  890.  
  891.       <!-- calICalendarView -->
  892.  
  893.       <property name="supportsDisjointDates" readonly="true"
  894.         onget="return false;"/>
  895.       <property name="hasDisjointDates" readonly="true"
  896.         onget="return false;"/>
  897.  
  898.       <property name="displayCalendar">
  899.         <getter><![CDATA[
  900.            return this.mCalendar;
  901.         ]]></getter>
  902.         <setter><![CDATA[
  903.            if (this.mCalendar != val) {
  904.              this.mCalendar = val;
  905.              this.mCalendar.addObserver(this.mObserver);
  906.              this.refresh();
  907.            }
  908.            return val;
  909.         ]]></setter>
  910.       </property>
  911.  
  912.       <property name="weekStartOffset">
  913.         <getter><![CDATA[
  914.            return this.mWeekStartOffset;
  915.         ]]></getter>
  916.         <setter><![CDATA[
  917.            this.mWeekStartOffset = val;
  918.            return val;
  919.         ]]></setter>
  920.       </property>
  921.  
  922.       <property name="displayDaysOff">
  923.         <getter><![CDATA[
  924.            return this.mDisplayDaysOff;
  925.         ]]></getter>
  926.         <setter><![CDATA[
  927.            this.mDisplayDaysOff = val;
  928.            return val;
  929.         ]]></setter>
  930.       </property>
  931.  
  932.       <property name="daysOffArray">
  933.         <getter><![CDATA[
  934.            return this.mDaysOffArray;
  935.         ]]></getter>
  936.         <setter><![CDATA[
  937.            this.mDaysOffArray = val;
  938.            return val;
  939.         ]]></setter>
  940.       </property>
  941.  
  942.       <property name="controller"
  943.     onget="return this.mController;"
  944.     onset="return this.mController = val;"/>
  945.  
  946.       <property name="startDate" readonly="true"
  947.     onget="return this.mStartDate"/>
  948.  
  949.       <property name="endDate" readonly="true">
  950.     <getter><![CDATA[
  951.       return this.mEndDate;
  952.     ]]></getter>
  953.       </property>
  954.  
  955.       <!-- the end date that should be used for getItems and similar queries -->
  956.       <property name="queryEndDate" readonly="true">
  957.         <getter><![CDATA[
  958.       var end = this.endDate;
  959.       if (!end)
  960.               return null;
  961.  
  962.           end = end.clone();
  963.           end.day += 1;
  964.           end.isDate = true;
  965.  
  966.           return end;
  967.         ]]></getter>
  968.       </property>
  969.  
  970.       <property name="tasksInView">
  971.         <getter><![CDATA[
  972.           return this.mTasksInView;
  973.         ]]></getter>
  974.         <setter><![CDATA[
  975.           this.mTasksInView = val;
  976.           return val;
  977.         ]]></setter>
  978.       </property>
  979.  
  980.       <property name="showCompleted">
  981.         <getter><![CDATA[
  982.           return this.mShowCompleted;
  983.         ]]></getter>
  984.         <setter><![CDATA[
  985.           this.mShowCompleted = val;
  986.           return val;
  987.         ]]></setter>
  988.       </property>
  989.  
  990.       <property name="showFullMonth">
  991.         <getter><![CDATA[
  992.           return this.mShowFullMonth;
  993.         ]]></getter>
  994.         <setter><![CDATA[
  995.           this.mShowFullMonth = val;
  996.           return val;
  997.         ]]></setter>
  998.       </property>
  999.  
  1000.       <method name="getSelectedItems">
  1001.         <parameter name="aCount"/>
  1002.         <body><![CDATA[
  1003.           aCount.value = this.mSelectedItems.length;
  1004.           return this.mSelectedItems;
  1005.         ]]></body>
  1006.       </method>
  1007.       <method name="setSelectedItems">
  1008.         <parameter name="aCount"/>
  1009.         <parameter name="aItems"/>
  1010.         <parameter name="aSuppressEvent"/>
  1011.         <body><![CDATA[
  1012.           if (this.mSelectedItems.length) {
  1013.               for each (var item in this.mSelectedItems) {
  1014.                   var oldboxes = this.findBoxesForItem(item);
  1015.                   for each (oldbox in oldboxes) {
  1016.                       oldbox.box.unselectItem(item);
  1017.                   }
  1018.               }
  1019.           }
  1020.  
  1021.           this.mSelectedItems = aItems || [];
  1022.  
  1023.           if (this.mSelectedItems.length) {
  1024.               for each (var item in this.mSelectedItems) {
  1025.                   var newboxes = this.findBoxesForItem(item);
  1026.                   for each (newbox in newboxes) {
  1027.                       newbox.box.selectItem(item);
  1028.                   }
  1029.               }
  1030.           }
  1031.  
  1032.           if (!aSuppressEvent) {
  1033.               this.fireEvent("itemselect", this.mSelectedItems);
  1034.           }
  1035.         ]]></body>
  1036.       </method>
  1037.  
  1038.       <property name="selectedDay">
  1039.         <getter><![CDATA[
  1040.           if (this.mSelectedDayBox)
  1041.             return this.mSelectedDayBox.date.clone();
  1042.  
  1043.           return null;
  1044.         ]]></getter>
  1045.         <setter><![CDATA[
  1046.           if (this.mSelectedDayBox)
  1047.             this.mSelectedDayBox.box.selected = false;
  1048.  
  1049.           var realVal = val;
  1050.           if (!realVal.isDate) {
  1051.             realVal = val.clone();
  1052.             realVal.isDate = true;
  1053.           }
  1054.           var box = this.findBoxForDate(realVal);
  1055.           if (box) {
  1056.             box.box.selected = true;
  1057.             this.mSelectedDayBox = box;
  1058.           }
  1059.           this.fireEvent("dayselect", realVal);
  1060.           return val;
  1061.         ]]></setter>
  1062.       </property>
  1063.  
  1064.       <property name="timezone">
  1065.         <getter><![CDATA[
  1066.           return this.mTimezone;
  1067.         ]]></getter>
  1068.         <setter><![CDATA[
  1069.           this.mTimezone = val;
  1070.           return val;
  1071.         ]]></setter>
  1072.       </property>
  1073.  
  1074.       <method name="fireEvent">
  1075.         <parameter name="aEventName"/>
  1076.         <parameter name="aEventDetail"/>
  1077.         <body><![CDATA[
  1078.           var event = document.createEvent('Events'); 
  1079.           event.initEvent(aEventName, true, false);
  1080.           event.detail = aEventDetail;
  1081.           this.dispatchEvent(event);
  1082.         ]]></body>
  1083.       </method>
  1084.  
  1085.       <method name="showDate">
  1086.         <parameter name="aDate"/>
  1087.         <body><![CDATA[
  1088.           aDate = aDate.getInTimezone(this.mTimezone);
  1089.  
  1090.           // We might need to do some adjusting here to make sure we still
  1091.           // display whole month even with alternative week-start days
  1092.           var startDate = aDate.startOfMonth;
  1093.           var endDate = aDate.endOfMonth;
  1094.           if (startDate.weekday < this.mWeekStartOffset) {
  1095.             // Go back one week to make sure we display this day
  1096.             startDate.day -= 7;
  1097.           }
  1098.  
  1099.           if (endDate.weekday < this.mWeekStartOffset) {
  1100.             // Go back one week so we don't display any extra days
  1101.             endDate.day -= 7;
  1102.           }
  1103.  
  1104.           this.setDateRange(startDate, endDate);
  1105.           this.selectedDay = aDate;
  1106.         ]]></body>
  1107.       </method>
  1108.  
  1109.       <method name="setDateRange">
  1110.         <parameter name="aStartDate"/>
  1111.         <parameter name="aEndDate"/>
  1112.         <body><![CDATA[
  1113.           if (this.mTimezone != aStartDate.timezone) {
  1114.             aStartDate = aStartDate.getInTimezone(this.mTimezone);
  1115.             aEndDate = aEndDate.getInTimezone(this.mTimezone);
  1116.           }
  1117.  
  1118.           this.mStartDate = aStartDate.startOfWeek;
  1119.           this.mEndDate = aEndDate.endOfWeek;
  1120.  
  1121.           this.mStartDate.day += this.mWeekStartOffset;
  1122.           this.mEndDate.day += this.mWeekStartOffset;
  1123.  
  1124.           this.refresh();
  1125.         ]]></body>
  1126.       </method>
  1127.  
  1128.       <method name="setDateList">
  1129.         <parameter name="aCount"/>
  1130.         <parameter name="aDates"/>
  1131.         <body><![CDATA[
  1132.            throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  1133.         ]]></body>
  1134.       </method>
  1135.  
  1136.       <method name="getDateList">
  1137.         <parameter name="aCount"/>
  1138.         <body><![CDATA[
  1139.       if (!this.mStartDate || !this.mEndDate) {
  1140.         aCount.value = 0;
  1141.         return [];
  1142.       }
  1143.  
  1144.       var results = [];
  1145.       var curDate = this.mStartDate.clone();
  1146.       curDate.isDate = true;
  1147.  
  1148.       while (curDate.compare(this.mEndDate) <= 0) {
  1149.         results.push(curDate.clone());
  1150.         curDate.day += 1;
  1151.       }
  1152.       aCount.value = results.length;
  1153.       return results;
  1154.         ]]></body>
  1155.       </method>
  1156.  
  1157.       <!-- public properties and methods -->
  1158.  
  1159.       <!-- whether to show days outside of the current month -->
  1160.       <property name="showDaysOutsideMonth">
  1161.         <getter><![CDATA[
  1162.           return this.mShowDaysOutsideMonth;
  1163.         ]]></getter>
  1164.         <setter><![CDATA[
  1165.           if (this.mShowDaysOutsideMonth != val) {
  1166.             this.mShowDaysOutsideMonth = val;
  1167.             this.refresh();
  1168.           }
  1169.           return val;
  1170.         ]]></setter>
  1171.       </property>
  1172.  
  1173.       <!-- private properties and methods -->
  1174.  
  1175.       <property name="monthgrid" readonly="true"
  1176.     onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'monthgrid');"/>
  1177.  
  1178.       <property name="monthgridrows" readonly="true"
  1179.     onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'monthgridrows');"/>
  1180.  
  1181.       <field name="mRefreshQueue">[]</field>
  1182.       <field name="mRefreshPending">null</field>
  1183.  
  1184.       <method name="popRefreshQueue">
  1185.         <body><![CDATA[
  1186.           var pendingRefresh = this.mRefreshPending;
  1187.           if (pendingRefresh) {
  1188.               if (pendingRefresh instanceof Components.interfaces.calIOperation) {
  1189.                   this.mRefreshPending = null;
  1190.                   pendingRefresh.cancel(null);
  1191.               } else {
  1192.                   if(this.mRefreshQueue.length > 0) {
  1193.                       this.relayout();
  1194.                   }
  1195.                   return;
  1196.               }
  1197.           }
  1198.  
  1199.           var refreshJob = this.mRefreshQueue.pop();
  1200.           if (!refreshJob) {
  1201.             return;
  1202.           }
  1203.  
  1204.           if (!this.startDate || !this.endDate)
  1205.             return;
  1206.  
  1207.           this.relayout();
  1208.  
  1209.           if (!this.mCalendar)
  1210.             return;
  1211.  
  1212.           var filter = this.mCalendar.ITEM_FILTER_CLASS_OCCURRENCES;
  1213.           if (this.showCompleted) {
  1214.             filter |= this.mCalendar.ITEM_FILTER_COMPLETED_ALL;
  1215.           } else {
  1216.             filter |= this.mCalendar.ITEM_FILTER_COMPLETED_NO;
  1217.           }
  1218.  
  1219.           if (this.mTasksInView)
  1220.             filter |= this.mCalendar.ITEM_FILTER_TYPE_ALL; 
  1221.           else
  1222.             filter |= this.mCalendar.ITEM_FILTER_TYPE_EVENT;
  1223.  
  1224.           this.mRefreshPending = true;
  1225.           pendingRefresh = this.mCalendar.getItems(filter,
  1226.                                                    0,
  1227.                                                    this.startDate,
  1228.                                                    this.queryEndDate,
  1229.                                                    this.mOperationListener);
  1230.           if (pendingRefresh && pendingRefresh.isPending) { // support for calIOperation
  1231.               this.mRefreshPending = pendingRefresh;
  1232.           }
  1233.         ]]></body>
  1234.       </method>
  1235.  
  1236.       <method name="refresh">
  1237.         <body><![CDATA[
  1238.           var refreshJob = {};
  1239.           this.mRefreshQueue.push(refreshJob);
  1240.           this.popRefreshQueue();
  1241.         ]]></body>
  1242.       </method>
  1243.  
  1244.       <method name="relayout">
  1245.     <body><![CDATA[
  1246.           function createXULElement(el) {
  1247.               return document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", el);
  1248.           }
  1249.  
  1250.           // Adjust headers based on the starting day of the week, if necessary
  1251.           var headerbox = document.getAnonymousElementByAttribute(this, "anonid", "headerbox");
  1252.  
  1253.           if (headerbox.firstChild.index != this.mWeekStartOffset) {
  1254.               var i = 0;
  1255.               for each(header in headerbox.childNodes) {
  1256.                 header.index = (i + this.mWeekStartOffset) % 7;
  1257.                 i++;
  1258.               }
  1259.           }
  1260.  
  1261.           if (this.mSelectedItems.length) {
  1262.             this.mSelectedItems = [];
  1263.           }
  1264.       
  1265.           // Clear out the old selection, since it won't be valid after relayout
  1266.           if (this.mSelectedDayBox) {
  1267.             this.mSelectedDayBox.box.selected = false;
  1268.           }
  1269.  
  1270.           if (!this.mStartDate || !this.mEndDate)
  1271.             throw NS_ERROR_FAILURE;
  1272.  
  1273.           // Days that are not in the main month on display are displayed with
  1274.           // a gray background.  Unless the month actually starts on a Sunday,
  1275.           // this means that mStartDate.month is 1 month less than the main month
  1276.           var mainMonth = this.mStartDate.month;
  1277.           if (this.mStartDate.day != 1) {
  1278.             mainMonth++;
  1279.             mainMonth = mainMonth % 12;
  1280.           }
  1281.  
  1282.           var dateBoxes = [];
  1283.           var today = this.today();
  1284.  
  1285.           // This gets set to true, telling us to collapse the rest of the rows
  1286.           var finished = false;
  1287.           var dateList = this.getDateList({})
  1288.  
  1289.           var rows = this.monthgridrows.childNodes;
  1290.  
  1291.           // Iterate through each monthgridrow and set up the day-boxes that 
  1292.           // are its child nodes.  Remember, childNodes is not a normal array,
  1293.           // so don't use the in operator if you don't want extra properties 
  1294.           // coming out.
  1295.           for (var i = 0; i < rows.length; i++) {
  1296.             var row = rows[i];
  1297.             // If we've already assigned all of the day-boxes that we need, just 
  1298.             // collapse the rest of the rows, otherwise expand them if needed.
  1299.             if (finished) {
  1300.               row.setAttribute("collapsed", true);
  1301.               continue;
  1302.             } else {
  1303.               row.removeAttribute("collapsed");
  1304.             }
  1305.             for (var j = 0; j < row.childNodes.length; j++) {
  1306.               var daybox = row.childNodes[j];
  1307.               var date = dateList[dateBoxes.length];
  1308.  
  1309.               daybox.setAttribute("context", this.getAttribute("context"));
  1310.               daybox.setAttribute("item-context", this.getAttribute("item-context") || this.getAttribute("context"));
  1311.  
  1312.               // Set the box-class depending on if this box displays a day in
  1313.               // the month being currently shown or not.
  1314.               var boxClass;
  1315.               if (this.showFullMonth) {
  1316.                   boxClass = "calendar-month-day-box-" + 
  1317.                              (mainMonth == date.month ? "current-month" : "other-month");
  1318.               } else {
  1319.                   boxClass = "calendar-month-day-box-current-month";
  1320.               }
  1321.  
  1322.               function matchesDayOff(dayOffNum) { return dayOffNum == date.weekday; }
  1323.               if (this.mDaysOffArray.some(matchesDayOff)) {
  1324.                 boxClass = "calendar-month-day-box-day-off " + boxClass;
  1325.               }
  1326.  
  1327.               // Set up date relations
  1328.               switch (date.compare(today)) {
  1329.                   case -1:
  1330.                       daybox.setAttribute("relation", "past");
  1331.                       break;
  1332.                   case 0:
  1333.                       daybox.setAttribute("relation", "today");
  1334.                       break;
  1335.                   case 1:
  1336.                       daybox.setAttribute("relation", "future");
  1337.                       break;
  1338.               }
  1339.  
  1340.               daybox.setAttribute("class", boxClass);
  1341.  
  1342.               daybox.setDate(date);
  1343.               if (date.day == 1 || date.day == date.endOfMonth.day) {
  1344.                 daybox.showMonthLabel = true;
  1345.               } else {
  1346.                 daybox.showMonthLabel = false;
  1347.               }
  1348.               daybox.monthView = this;
  1349.  
  1350.               // add the box and its data to our stored array
  1351.               var boxdata = {
  1352.                 date: date,
  1353.                 row: row,
  1354.                 box: daybox
  1355.               };
  1356.  
  1357.               dateBoxes.push(boxdata);
  1358.  
  1359.               // If we've now assigned all of our dates, set this to true so we
  1360.               // know we can just collapse the rest of the rows.
  1361.               if (dateBoxes.length == dateList.length) {
  1362.                 finished = true;
  1363.               }
  1364.             }
  1365.           }
  1366.  
  1367.           // If we're not showing a full month, then add a few extra labels to
  1368.           // help the user orient themselves in the view.
  1369.           if (!this.mShowFullMonth) {
  1370.             dateBoxes[0].box.showMonthLabel = true;
  1371.             dateBoxes[dateBoxes.length-1].box.showMonthLabel = true;
  1372.           }
  1373.  
  1374.           // Store these, so that we can access them later
  1375.           this.mDateBoxes = dateBoxes;
  1376.           this.hideDaysOff();
  1377.  
  1378.           // Highlight today, if it's in the range of the view
  1379.           if (today.compare(dateList[0]) != -1 &&
  1380.               today.compare(dateList[dateList.length-1]) != 1) {
  1381.               this.findBoxForDate(today).box.setAttribute("today", "true");
  1382.           }
  1383.         ]]></body>
  1384.       </method>
  1385.  
  1386.       <method name="hideDaysOff">
  1387.         <body><![CDATA[
  1388.           var columns = document.getAnonymousElementByAttribute(this, "anonid", "monthgridcolumns").childNodes;
  1389.           var headerkids = document.getAnonymousElementByAttribute(this, "anonid", "headerbox").childNodes;
  1390.           for (var i in columns) {
  1391.             var dayForColumn = (Number(i) + this.mWeekStartOffset) % 7;
  1392.             var dayOff = (this.mDaysOffArray.indexOf(dayForColumn) != -1);
  1393.             columns[i].collapsed = dayOff && !this.mDisplayDaysOff;
  1394.             headerkids[i].collapsed = dayOff && !this.mDisplayDaysOff;
  1395.           }
  1396.         ]]></body>
  1397.       </method>
  1398.  
  1399.       <method name="findBoxForDate">
  1400.         <parameter name="aDate"/>
  1401.         <body><![CDATA[
  1402.           for each (box in this.mDateBoxes) {
  1403.             if (box.date.compare(aDate) == 0)
  1404.               return box;
  1405.           }
  1406.           return null;
  1407.         ]]></body>
  1408.       </method>
  1409.  
  1410.       <method name="findBoxesForItem">
  1411.         <parameter name="aItem"/>
  1412.         <body><![CDATA[
  1413.           var targetDate = null;
  1414.           var finishDate = null;
  1415.           var boxes = new Array();
  1416.  
  1417.           // All our boxes are in default tz, so we need these times in them too.
  1418.           if (isEvent(aItem)) {
  1419.             targetDate = aItem.startDate.getInTimezone(this.mTimezone);
  1420.             finishDate = aItem.endDate.getInTimezone(this.mTimezone);
  1421.           } else if (isToDo(aItem)) {
  1422.             if (aItem.entryDate) {
  1423.               targetDate = aItem.entryDate.getInTimezone(this.mTimezone);
  1424.               if (aItem.dueDate) {
  1425.                 finishDate = aItem.dueDate.getInTimezone(this.mTimezone);
  1426.               }
  1427.             }
  1428.           }
  1429.  
  1430.           if (!targetDate)
  1431.             return boxes;
  1432.  
  1433.           if (!finishDate) {
  1434.             var maybeBox = this.findBoxForDate(targetDate);
  1435.             if (maybeBox) {
  1436.               boxes.push(maybeBox);
  1437.             }
  1438.             return boxes;
  1439.           }
  1440.  
  1441.           if (!targetDate.isDate) {
  1442.             // Reset the time to 00:00, so that we really get all the boxes
  1443.             targetDate.hour = 0;
  1444.             targetDate.minute = 0;
  1445.             targetDate.second = 0;
  1446.           }
  1447.  
  1448.           if (targetDate.compare(finishDate) == 0) {
  1449.               // Zero length events are silly, but we have to handle them
  1450.               var box = this.findBoxForDate(targetDate);
  1451.               if (box) {
  1452.                   boxes.push(box);
  1453.               }
  1454.           }
  1455.  
  1456.           while (targetDate.compare(finishDate) == -1) {
  1457.             var box = this.findBoxForDate(targetDate);
  1458.  
  1459.             // This might not exist, if the event spans the view start or end
  1460.             if (box) {
  1461.                 boxes.push(box);
  1462.             }
  1463.             targetDate.day += 1;
  1464.           }
  1465.  
  1466.           return boxes;
  1467.     ]]></body>
  1468.       </method>
  1469.  
  1470.       <method name="doAddItem">
  1471.         <parameter name="aItem"/>
  1472.         <body><![CDATA[
  1473.           var boxes = this.findBoxesForItem(aItem);
  1474.  
  1475.           if (!boxes.length)
  1476.             return;
  1477.  
  1478.           for each (box in boxes) {
  1479.             box.box.addItem(aItem);
  1480.           }
  1481.         ]]></body>
  1482.       </method>
  1483.  
  1484.       <method name="doDeleteItem">
  1485.         <parameter name="aItem"/>
  1486.         <body><![CDATA[
  1487.           var boxes = this.findBoxesForItem(aItem);
  1488.  
  1489.           if (!boxes.length)
  1490.             return;
  1491.  
  1492.           function isNotItem(a) {
  1493.               return (a.hashId != aItem.hashId);
  1494.           }
  1495.           var oldLength = this.mSelectedItems.length;
  1496.           this.mSelectedItems = this.mSelectedItems.filter(isNotItem);
  1497.  
  1498.           for each (box in boxes) {
  1499.             box.box.deleteItem(aItem);
  1500.           }
  1501.  
  1502.           // If a deleted event was selected, we need to announce that the
  1503.           // selection changed.
  1504.           if (oldLength != this.mSelectedItems.length) {
  1505.               this.fireEvent("itemselect", this.mSelectedItems);
  1506.           }
  1507.         ]]></body>
  1508.       </method>
  1509.  
  1510.       <method name="today">
  1511.         <body><![CDATA[
  1512.           var date = createDateTime();
  1513.           date.jsDate = new Date();
  1514.           date = date.getInTimezone(this.mTimezone);
  1515.           date.isDate = true;
  1516.           return date;
  1517.         ]]></body>
  1518.       </method>
  1519.  
  1520.       <!-- our private observers and listeners -->
  1521.  
  1522.       <field name="mOperationListener"><![CDATA[
  1523.         ({
  1524.           calView: this,
  1525.  
  1526.           QueryInterface: function (aIID) {
  1527.             if (!aIID.equals(Components.interfaces.calIOperationListener) &&
  1528.                 !aIID.equals(Components.interfaces.nsISupports)) {
  1529.               throw Components.results.NS_ERROR_NO_INTERFACE;
  1530.             }
  1531.  
  1532.             return this;
  1533.           },
  1534.   
  1535.           onOperationComplete: function(aCalendar, aStatus, aOperationType, aId, aDetail) {
  1536.             // Fire viewloaded event
  1537.             this.calView.fireEvent('viewloaded', aOperationType);
  1538.           
  1539.             // signal that the current operation finished.
  1540.             this.calView.mRefreshPending = null;
  1541.  
  1542.             // immediately start the next job on the queue.
  1543.             this.calView.popRefreshQueue();
  1544.           },
  1545.           onGetResult: function(aCalendar, aStatus, aItemType, aDetail, aCount, aItems) {
  1546.             if (!Components.isSuccessCode(aStatus))
  1547.               return;
  1548.   
  1549.             for each (var item in aItems) {
  1550.               this.calView.doAddItem(item);
  1551.             }
  1552.           }
  1553.         })
  1554.       ]]></field>
  1555.  
  1556.       <field name="mObserver"><![CDATA[
  1557.         // the calIObserver, calICompositeObserver, calIAlarmServiceObserver
  1558.         ({
  1559.           calView: this,
  1560.           mBatchCount: 0,
  1561.  
  1562.           QueryInterface: function (aIID) {
  1563.             if (!aIID.equals(Components.interfaces.calIObserver) &&
  1564.                 !aIID.equals(Components.interfaces.calICompositeObserver) &&
  1565.                 !aIID.equals(Components.interfaces.calIAlarmServiceObserver) &&
  1566.                 !aIID.equals(Components.interfaces.nsISupports)) {
  1567.               throw Components.results.NS_ERROR_NO_INTERFACE;
  1568.             }
  1569.  
  1570.             return this;
  1571.           },
  1572.  
  1573.           onStartBatch: function() {
  1574.             this.mBatchCount++;
  1575.           },
  1576.           onEndBatch: function() {
  1577.             this.mBatchCount--;
  1578.             if (this.mBatchCount == 0) {
  1579.               this.calView.refresh();
  1580.             }
  1581.           },
  1582.           onLoad: function() {
  1583.             this.calView.refresh();
  1584.           },
  1585.           onAddItem: function (aItem) {
  1586.             if (this.mBatchCount) {
  1587.                 return;
  1588.             }
  1589.  
  1590.             if (isToDo(aItem)) {
  1591.               if (!this.calView.mTasksInView) {
  1592.                 return;
  1593.               }
  1594.               if (aItem.isCompleted && !this.calView.mShowCompleted) {
  1595.                 return;
  1596.               }
  1597.             }
  1598.  
  1599.             var occs = aItem.getOccurrencesBetween(this.calView.startDate,
  1600.                                                    this.calView.queryEndDate,
  1601.                                                    {});
  1602.             for each (var occ in occs)
  1603.               this.calView.doAddItem(occ);
  1604.           },
  1605.           onModifyItem: function (aNewItem, aOldItem) {
  1606.             if (this.mBatchCount) {
  1607.                 return;
  1608.             }
  1609.  
  1610.             if (isToDo(aNewItem) && isToDo(aOldItem) &&
  1611.                 !this.calView.mTasksInView) {
  1612.                 return;
  1613.             }
  1614.  
  1615.             var occs;
  1616.             occs = aOldItem.getOccurrencesBetween(this.calView.startDate,
  1617.                                                   this.calView.queryEndDate,
  1618.                                                   {});
  1619.             for each (var occ in occs)
  1620.               this.calView.doDeleteItem(occ);
  1621.  
  1622.             if (isToDo(aNewItem)) {
  1623.               if (!this.calView.mTasksInView) {
  1624.                 return;
  1625.               }
  1626.               if (aNewItem.isCompleted && !this.calView.mShowCompleted) {
  1627.                 return;
  1628.               }
  1629.             }
  1630.             occs = aNewItem.getOccurrencesBetween(this.calView.startDate,
  1631.                                                   this.calView.queryEndDate,
  1632.                                                   {});
  1633.             for each (var occ in occs)
  1634.               this.calView.doAddItem(occ);
  1635.           },
  1636.           onDeleteItem: function (aItem) {
  1637.             if (this.mBatchCount) {
  1638.                 return;
  1639.             }
  1640.  
  1641.             if (isToDo(aItem)) {
  1642.               if (!this.calView.mTasksInView) {
  1643.                 return;
  1644.               }
  1645.               if (aItem.isCompleted && !this.calView.mShowCompleted) {
  1646.                 return;
  1647.               }
  1648.             }
  1649.  
  1650.             var occs = aItem.getOccurrencesBetween(this.calView.startDate,
  1651.                                                    this.calView.queryEndDate,
  1652.                                                    {});
  1653.             for each (var occ in occs) {
  1654.               this.calView.doDeleteItem(occ);
  1655.             }
  1656.           },
  1657.           onError: function (aErrNo, aMessage) { },
  1658.  
  1659.           onPropertyChanged: function(aCalendar, aName, aValue, aOldValue) {
  1660.             if (aName == "suppressAlarms" &&
  1661.                 aCalendar.getProperty("capabilities.alarms.popup.supported") !== false &&
  1662.                 getPrefSafe("calendar.alarms.indicator.show", true)) {
  1663.               this.calView.refresh();
  1664.             }
  1665.           },
  1666.           onPropertyDeleting: function(aCalendar, aName) {
  1667.             this.onPropertyChanged(aCalendar, aName, null, null);
  1668.           },
  1669.  
  1670.           //
  1671.           // calIAlarmServiceObserver stuff
  1672.           //
  1673.           onAlarm: function onAlarm(aAlarmItem) {
  1674.             this.calView.flashAlarm(aAlarmItem, false);
  1675.           },
  1676.  
  1677.           onRemoveAlarmsByItem: function onRemoveAlarmsByItem(aItem) {
  1678.               // Stop the flashing for the item.
  1679.               this.calView.flashAlarm(aItem, true);
  1680.           },
  1681.  
  1682.           onRemoveAlarmsByCalendar: function onRemoveAlarmsByCalendar(aCalendar) {
  1683.               // Stop the flashing for all items of this calendar
  1684.               for each (var item in this.calView.mFlashingEvents) {
  1685.                   if (item.calendar.id == aCalendar.id) {
  1686.                       this.calView.flashAlarm(item, true);
  1687.                   }
  1688.               }
  1689.           },
  1690.  
  1691.           //
  1692.           // calICompositeObserver stuff
  1693.           // XXXvv we can be smarter about how we handle this stuff
  1694.           //
  1695.           onCalendarAdded: function (aCalendar) {
  1696.             this.calView.refresh();
  1697.           },
  1698.  
  1699.           onCalendarRemoved: function (aCalendar) {
  1700.             this.calView.refresh();
  1701.           },
  1702.  
  1703.           onDefaultCalendarChanged: function (aNewDefaultCalendar) {
  1704.                 // don't care, for now
  1705.           }
  1706.         })
  1707.       ]]></field>
  1708.  
  1709.       <method name="flashAlarm">
  1710.         <parameter name="aAlarmItem"/>
  1711.         <parameter name="aStop"/>
  1712.         <body><![CDATA[
  1713.           var showIndicator = getPrefSafe("calendar.alarms.indicator.show", true);
  1714.           var totaltime = getPrefSafe("calendar.alarms.indicator.totaltime", 3600);
  1715.  
  1716.           if (!aStop && (!showIndicator || totaltime < 1)) {
  1717.             // No need to animate if the indicator should not be shown.
  1718.             return;
  1719.           }
  1720.  
  1721.           // Make sure the flashing attribute is set or reset on all visible
  1722.           // boxes.
  1723.           var boxes = this.findBoxesForItem(aAlarmItem);
  1724.           for each (var box in boxes) {
  1725.             for each (var itemData in box.box.mItemData) {
  1726.               if (itemData.item.hasSameIds(aAlarmItem)) {
  1727.                 if (aStop) {
  1728.                   itemData.box.removeAttribute("flashing");
  1729.                 } else {
  1730.                   itemData.box.setAttribute("flashing", "true");
  1731.                 }
  1732.               }
  1733.             }
  1734.           }
  1735.  
  1736.           if (!aStop) {
  1737.             // Set up a timer to stop the flashing after the total time.
  1738.             var this_ = this;
  1739.             this.mFlashingEvents[aAlarmItem.hashId] = aAlarmItem;
  1740.             setTimeout(function() { this_.flashAlarm(aAlarmItem, true) }, totaltime);
  1741.           } else {
  1742.             // We are done flashing, prevent newly created event boxes from flashing.
  1743.             delete this.mFlashingEvents[aAlarmItem.hashId];
  1744.           }
  1745.         ]]></body>
  1746.       </method>
  1747.     </implementation>
  1748.  
  1749.     <handlers>
  1750.       <handler event="keypress"><![CDATA[
  1751.         const kKE = Components.interfaces.nsIDOMKeyEvent;
  1752.         if (event.keyCode == kKE.DOM_VK_BACK_SPACE ||
  1753.             event.keyCode == kKE.DOM_VK_DELETE)
  1754.         {
  1755.             if (!this.activeInPlaceEdit && this.mSelectedItems.length && this.controller) {
  1756.                 this.controller.deleteOccurrences(this.mSelectedItems.length,
  1757.                                                   this.mSelectedItems,
  1758.                                                   event.ctrlKey,
  1759.                                                   false);
  1760.             }
  1761.         }
  1762.       ]]></handler>
  1763.     </handlers>
  1764.   </binding>
  1765.  
  1766. </bindings>
  1767.